3DsMax导出插件编写(三)

您所在的位置:网站首页 3dmax uv导出 3DsMax导出插件编写(三)

3DsMax导出插件编写(三)

2024-01-03 12:40| 来源: 网络整理| 查看: 265

之前介绍过用SDK的常规方法来获取模型的网格信息。这里再介绍另外一种方法。 MaxSdk里面带了一个叫做IGame的包,里面包含有很多方便我们获取模型信息的方法。在sdk的自带例子里面,同样也有这个IGame的例子,不过我自己没有编译通过,存在一些错误。所以我自己看SDK的API,摸索出了使用的方法。

#include 。

使用IGame之前需要先给他做一个初始化,以下是我的初始化的方法,直接在DoExport方法里面先调用就可以了:

void InitIGame(const TCHAR *name,ExpInterface *ei,Interface *i, BOOL suppressPrompts, DWORD options) { //保存路径 savePath = name; //是否支持显示界面 showPrompts = suppressPrompts ? FALSE : TRUE; //导出选中物体还是导出全部 exportSelected = (options & SCENE_EXPORT_SELECTED) ? true : false; //获取IGame的版本,这个如果你用不到可以不要 igameVersion = GetIGameVersion(); //IGameScene* pScene;这个东西就是代替了常规方法里面的场景树节点管理 pScene = GetIGameInterface(); //获取IGameConversionManager 是为了设置左右手坐标系,如果你不需要设置,可以不要。 //具体的SetUserCoordSystem数值可以去查maxSdk的api,有详细说明 IGameConversionManager * cm = GetConversionManager(); cm->SetUserCoordSystem(leftHandCoord); //初始化场景 pScene->InitialiseIGame(exportSelected); } 这里和常规方法做一个对比,这个初始化的方法其实和常规方法差不多,不过方便的地方在于,我们可以直接的使用IGameScene类来管理场景,而不需要自己重新写一个树节点管理类,再自己写回调的方法了。然后可以设置左右手坐标系。常规方法如果你要转换坐标系,就要自己在导出的时间计算一下,调换一下坐标系的值了。最后,之前我们做是否导出选择的物体,需要自己在回调的方法里面判断exportSelected,但IGame却是在pScene->InitialiseIGame(exportSelected);这一步就已经把这件事完全的做好了。如果你是选择选中的物体导出,那么初始化的时候只会把选中的物体放进场景树节点里面,没有选择的东西是没有任何的信息的。这样做,好处是方便了操作,坏处是你想只导出选中的物体,又想遍历一些没有选中的物体做计算的时候,就不行了,但常规方法却可以。 接下来再看看怎样获取物体节点的各种信息: void CollectObjects() { int num = pScene->GetTopLevelNodeCount(); IGameNode* pNode; for(int i = 0; i < num; i++) { pNode = pScene->GetTopLevelNode(i); if(pNode->IsTarget()) { continue; } IGameObject* obj = pNode->GetIGameObject(); char* oName = pNode->GetName(); IGameObject::ObjectTypes type = obj->GetIGameType(); switch (type) { case IGameObject::IGAME_MESH: meshObjRoots.push_back(pNode); DebugLog(oName); DebugLog(" is a Mesh!!\n"); break; case IGameObject::IGAME_BONE: boneObjRoots.push_back(pNode); DebugLog(oName); DebugLog(" is a Bone!!\n"); break; default: break; } }

} 以上的方法,就可以获取到场景里面的所有作为根节点的物体了。所谓的根节点,就是父物体为空(也就是父物体为场景Root)的物体,它有可能还有很多子物体,这里我们先不获取,然后我们下一步可以通过获取子物体的方法做递归,就可以获取全部的物体了。 在这里我们先来和常规方法对比一下: 常规方法我们要判断物体的类型,需要判断节点物体的ClassID(),而且ClassID()的类别繁多,骨骼还分为bone和biped需要分别作判断。而IGame却比较方便了,直接GetIGameType,然后就可以判断类型,而且不管是bone还是biped,它都是IGAME_BONE,比较方便。

获得了父物体之后,我们可以写一个方法来遍历他们的子物体,做一个递归,最后返回的数据就是当前父物体的所有子物体了,自己想办法存起来

vector getChildNode(IGameNode* pObj) { vector tempList; tempList.push_back(pObj); int childNum = pObj->GetChildCount(); if(childNum>0) { for(int i = 0;iGetNodeChild(i); vector subVector = getChildNode(subNode); if(subVector.size()>0) { for(int j = 0;jGetIGameObject(); IGameMesh * iMesh = (IGameMesh*)iObj; iMesh->InitializeData();//这个就是初始化数据 如果不先这样初始化一下IGameObject,那么你之后获取模型信息的时候,很可能会遇到顶点索引是空的,之类的问题。 获取顶点信息:

int vertSum = iMesh->GetNumberOfVerts(); for(i = 0;iGetVertex(i); //然后自己存起来 }

获取顶点索引的信息:

int faceSum = iMesh->GetNumberOfFaces(); for(i = 0;iGetFace(i); vector indexs; for(j = 0;jvert[j]); } //然后自己存起来 }

获取UV信息:获取uv信息其实也是用上面的FaceEx方法的数据,不过由于3DsMax的贴图通道可能有很多个,我们习惯上就用第一个通道的,不过有些人会使用多套UV,可能会使用多个通道,所以我们先要获取一下所有的UV通道:

Tab mapNums = iMesh->GetActiveMapChannelNum();

这样,这里面就存了可能用到的UV通道的序号。

int mapCount = mapNums.Count(); for(int i=0;i < mapCount;i++) { for(j = 0;jGetFace(j); for(int k = 0;kvert[k]; int induv = face->texCoord[k]; Point3 tv; if(iMesh->GetMapVertex(mapNums[i],induv,tv)) { if(indtexCoord来获取UV坐标。其实他们的道理是一样的。 本来我们知道了顶点的总数,逐个去遍历他们的GetMapVertex,应该就可以获取到所有的顶点uv坐标,这是没错的。不过问题在于,顶点的位移坐标的排序,和uv坐标的排序是不一致的。有可能一个点在位移坐标时的排序index是10,但在uv坐标的排序index是20之类。所以如果你按照统一的一个顺序去遍历所有点再记录位移坐标和uv坐标,获取的数据是对的,但顺序很可能就错了,而导致之后你的模型的点和uv点对不上,导致了模型能显示出来,但uv贴图乱了。 这个uv坐标的排序index是怎样获得的呢?常规SDK是用TVFace和一般的Face相对应,然后获取Face对于的TVFace的顶点序号。而用IGame,它没有两种Face了,就只有一种FaceEx,里面的 face->vert是顶点位移的排序坐标,然后face->texCoord就是该位移顶点对应的uv坐标的排序了。

以上就是使用IGame获取模型网格信息的方法了。需要获取其他的数据,可以去查查api,里面都有说明。然后我们还是用C++提供的写文本方法,把数据保存起来就可以了。 接下来我将会再介绍一下骨骼动画的导出方法,



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3